use crate::exchange::lib::{CONFIG, TICKER, Ticker, callback};
use crate::utils::{READ_BUFFER_SIZE, WRITE_BUFFER_SIZE, create_text_frame, parse_frame, start_ws};
use sonic_rs::{JsonValueTrait, json, pointer};
use std::sync::Arc;
use std::time::Duration;
use tokio::io::{AsyncWriteExt, BufReader, BufWriter};

pub async fn ws(symbols: Arc<Vec<String>>) {
    let mut s = Vec::with_capacity(symbols.len());

    for symbol in &*symbols {
        s.push(json!({
            "instType": "USDT-FUTURES",
            "channel": "ticker",
            "instId": symbol,
        }));
    }

    let sub_msg = json!({
        "op": "subscribe",
        "args": s,
    });

    loop {
        let (rx, tx) = start_ws(
            "ws.bitget.com",
            Some("/v2/ws/public"),
            None,
            CONFIG.proxy.as_str(),
        )
        .await;

        let mut buf_reader = BufReader::with_capacity(READ_BUFFER_SIZE, rx);
        let mut buf_writer = BufWriter::with_capacity(WRITE_BUFFER_SIZE, tx);

        let msg = create_text_frame(sub_msg.to_string().as_str());
        buf_writer.write_all(&msg).await.unwrap();
        buf_writer.flush().await.unwrap();

        tokio::spawn(async move {
            let mut interval = tokio::time::interval(Duration::from_secs(30));
            loop {
                interval.tick().await;
                let ping_frame = create_text_frame("ping");

                if buf_writer.write_all(&ping_frame).await.is_err() {
                    break;
                }
                if buf_writer.flush().await.is_err() {
                    break;
                }
            }
        });

        loop {
            match parse_frame(&mut buf_reader).await {
                Ok((opcode, payload)) => {
                    match opcode {
                        0x1 => {
                            let Ok(obj) = (unsafe {
                                sonic_rs::get_from_slice_unchecked(&payload, pointer!["data", 0])
                            }) else {
                                continue;
                            };

                            let timestamp = obj
                                .get("ts")
                                .unwrap()
                                .as_str()
                                .unwrap()
                                .parse::<i64>()
                                .unwrap();

                            let bid = obj
                                .get("bidPr")
                                .unwrap()
                                .as_str()
                                .unwrap()
                                .parse::<f64>()
                                .unwrap();
                            let bid_amount = obj
                                .get("bidSz")
                                .unwrap()
                                .as_str()
                                .unwrap()
                                .parse::<f64>()
                                .unwrap();

                            let ask = obj
                                .get("askPr")
                                .unwrap()
                                .as_str()
                                .unwrap()
                                .parse::<f64>()
                                .unwrap();
                            let ask_amount = obj
                                .get("askSz")
                                .unwrap()
                                .as_str()
                                .unwrap()
                                .parse::<f64>()
                                .unwrap();

                            let symbol = obj.get("instId").unwrap().as_str().unwrap().to_string();

                            let mut value = TICKER
                                .entry(symbol.clone())
                                .or_insert_with(|| Ticker::default());

                            value.coinex = (bid, ask);
                            value.coinex_timestamp = timestamp;
                            value.coinex_bid_amount = bid_amount;
                            value.coinex_ask_amount = ask_amount;

                            drop(value);

                            callback(symbol).await;
                        }
                        0x2 => {
                            // 二进制帧
                            println!("收到二进制数据，长度: {}", payload.len());
                        }
                        0x8 => {
                            // 关闭帧
                            println!("连接关闭");
                            break;
                        }
                        0x9 => {
                            println!("收到ping帧");
                        }
                        0xa => {
                            println!("收到pong帧");
                        }
                        _ => {
                            println!("收到其他帧，opcode: {opcode}");
                        }
                    }
                }
                Err(e) => {
                    println!("读取错误: {e}");
                    tokio::time::sleep(Duration::from_secs(1)).await;
                    break;
                }
            }
        }
    }
}
